package org.example.redpacket.service.impl;


import org.example.redpacket.common.exception.*;
import org.example.redpacket.dao.ActivityMapper;
import org.example.redpacket.model.dto.RedPacketActivityDTO;
import org.example.redpacket.model.dto.UserReceiveRedPacketDTO;
import org.example.redpacket.model.entity.ActivityEntity;
import org.example.redpacket.model.entity.UserRedPacketDetailEntity;
import org.example.redpacket.model.entity.UserRedPacketEntity;
import org.example.redpacket.model.po.ActivityPO;
import org.example.redpacket.service.ActivityService;
import org.example.redpacket.service.UserRedPacketDetailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 活动相关的 实现类
 *
 * @author: hero生仔
 */
@Service
public class ActivityServiceImpl implements ActivityService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ActivityServiceImpl.class);

    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private UserRedPacketDetailService userRedPacketDetailService;
    @Autowired
    private UserRedPacketServiceImpl userRedPacketService;

    @Override
    public BigDecimal queryActivityAmountLeft(String activityId) {
        List<BigDecimal> amountLeftList = activityMapper.queryActivityAmountLeft(activityId);
        if (amountLeftList == null || amountLeftList.isEmpty()) {
            throw new ActivityEmptyException("活动" + activityId + "没上线");
        }
        return Optional.of(amountLeftList.stream().reduce(BigDecimal.ZERO, BigDecimal::add)).orElse(new BigDecimal("-1"));
    }

    @Override
    public Integer batchInsertRedPacketActivity(List<RedPacketActivityDTO> list) {
        // 判断库存中是否已有对应的活动
        List<ActivityPO> activityPOList = activityMapper.queryActivity(list.get(0).getActivityId());
        if (activityPOList == null || !activityPOList.isEmpty()) {
            throw new ActivityExistException("活动" + list.get(0).getActivityId() + "已存在");
        }
        ArrayList<ActivityPO> activityPOArrayList = new ArrayList<>();
        for (RedPacketActivityDTO activityDTO : list) {
            ActivityPO activityPO = ActivityPO.builder()
                    .activityId(activityDTO.getActivityId())
                    .totalPoolAmount(activityDTO.getAmount())
                    .poolLeftAmount(activityDTO.getLeftAmount())
                    .build();
            activityPOArrayList.add(activityPO);
        }

        // 对外来说是插入一个活动，但是对内来说只是插入了n个小活动
        Integer insertCount;
        try {
            insertCount = activityMapper.batchInsertRedPacketActivity(activityPOArrayList);
        } catch (Exception e) {
            LOGGER.error("数据库插入重复异常");
            throw new AlreadyInsertException(e.getLocalizedMessage(), e);
        }
        if (insertCount != list.size()) {
            LOGGER.error("数据库插入条数异常");
            insertCount = 0;
            // todo 异步删除之前添加的活动记录

        } else {
            insertCount = 1;
        }
        return insertCount;
    }

    @Override
    public ActivityPO qryActivity(String activityId) {
        List<ActivityPO> activityPOList = activityMapper.queryActivity(activityId);
        if (activityPOList == null || activityPOList.isEmpty()) {
            return null;
        }
        return activityPOList.get(0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, noRollbackFor = {AmountNotLeftException.class, TakeCountException.class}, timeout = 5000)
    public BigDecimal receiveRedPacket(UserReceiveRedPacketDTO userReceiveRedPacketDTO) {
        String[] split = userReceiveRedPacketDTO.getUserId().split("_");
        int idx = Integer.parseInt(split[1]) % 10;
        String activityId = userReceiveRedPacketDTO.getActivityId() + "_" + idx;
        // 查询是否有对应的活动
        List<ActivityPO> activityPOList = activityMapper.queryActivity(userReceiveRedPacketDTO.getActivityId());
        if (activityPOList.isEmpty()) {
            return BigDecimal.ZERO;
        }
        ConcurrentMap<String, ActivityEntity> entityConcurrentMap = activityPOList.stream()
                .map(activityPO ->
                        ActivityEntity.builder()
                                .id(activityPO.getId())
                                .activityId(activityPO.getActivityId())
                                .totalPoolAmount(activityPO.getTotalPoolAmount())
                                .poolLeftAmount(activityPO.getPoolLeftAmount())
                                .createTime(activityPO.getCreateTime())
                                .updateTime(activityPO.getUpdateTime())
                                .build()).collect(Collectors.toConcurrentMap(ActivityEntity::getActivityId, Function.identity()));

        // 查询该分活动的余额是否充足
        ActivityEntity activityEntity = entityConcurrentMap.get(activityId);
        if (activityEntity.getPoolLeftAmount().compareTo(new BigDecimal("0.01")) < 0) {
            throw new AmountNotLeftException("该分奖池的余额小于 0.01 元，不足以抽奖");
        }

        // 查询该活动中该用户领取次数是否超过10次
        Map<String, Object> userRedPacketParams = new HashMap<>();
        userRedPacketParams.put("activityId", userReceiveRedPacketDTO.getActivityId());
        userRedPacketParams.put("userId", userReceiveRedPacketDTO.getUserId());
        Integer count = userRedPacketService.queryReceiveRedPacketCount(userRedPacketParams);
        count = Optional.ofNullable(count).orElse(0);
        int insertUserRedPacketNum;
        if (count >= 10) {
            throw new TakeCountException("该用户" + userReceiveRedPacketDTO.getUserId() + "在该次活动中已领取10次，不足以抽奖");
        } else if (count == 0) {
            // 更新用户红包关系表，首次添加这个用户的信息
            UserRedPacketEntity userRedPacketEntity = UserRedPacketEntity.builder()
                    .userId(userReceiveRedPacketDTO.getUserId())
                    .activityId(userReceiveRedPacketDTO.getActivityId())
                    .number(1)
                    .build();
            insertUserRedPacketNum = userRedPacketService.insertUserRedPacket(userRedPacketEntity);
            if (insertUserRedPacketNum != 1) {
                throw new InsertOrUpdateException("插入记录失败");
            }
        } else {
            // 更新用户红包关系表
            UserRedPacketEntity userRedPacketEntity = UserRedPacketEntity.builder()
                    .userId(userReceiveRedPacketDTO.getUserId())
                    .activityId(userReceiveRedPacketDTO.getActivityId())
                    .number(count + 1)
                    .build();
            insertUserRedPacketNum = userRedPacketService.insertUserRedPacket(userRedPacketEntity);
            if (insertUserRedPacketNum != 1) {
                throw new InsertOrUpdateException("更新记录失败");
            }
        }

        BigDecimal randomAmount = randomAmount(activityEntity.getPoolLeftAmount());
        // 奖池中进行扣除
        Map<String, Object> params = new HashMap<>();
        params.put("activityId", activityId);
        params.put("poolLeftAmount", randomAmount);
        int updateNum, insertUserRedPacketDetailNum;
        try {
            updateNum = activityMapper.updateLeftAmount(params);
            if (updateNum != 1) {
                throw new InsertOrUpdateException("更新记录失败");
            }

            // 更新用户领取红包明细表
            UserRedPacketDetailEntity userRedPacketDetailEntity = UserRedPacketDetailEntity.builder()
                    .userId(userReceiveRedPacketDTO.getUserId())
                    .activityId(userReceiveRedPacketDTO.getActivityId())
                    .redPacketId(userReceiveRedPacketDTO.getRedPacketId())
                    .useStatus(0)
                    .receivedAmount(randomAmount)
                    .build();
            insertUserRedPacketDetailNum = userRedPacketDetailService.insertUserRedPacketDetail(userRedPacketDetailEntity);
            if (insertUserRedPacketDetailNum != 1) {
                throw new InsertOrUpdateException("插入记录失败");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return randomAmount;
    }

    /**
     * 随机生成一个金额
     *
     * @param leftAmount 剩余金额
     * @return 返回一个随机金额
     */
    private BigDecimal randomAmount(BigDecimal leftAmount) {
        BigDecimal randomAmount;
        // 剩余的奖金要是小于 1 元的，右区间则是 传来的参数，否则按照[0.01,1)取值即可
        if (leftAmount.compareTo(new BigDecimal("0.01")) == 0) {
            throw new AmountNotLeftException("该分奖池的余额小于 0.01 元，不足以抽奖");
        } else if (leftAmount.compareTo(BigDecimal.ONE) < 0) {
            randomAmount = BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble(0.01, leftAmount.doubleValue())).setScale(2, RoundingMode.HALF_UP);
        } else {
            randomAmount = BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble(0.01, 1)).setScale(2, RoundingMode.HALF_UP);
        }
        return randomAmount;
    }
}
